﻿using BUGCOME.Infrastructure.Helper;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BUGCOME.Common.DynamicApiSimple
{
    class ApiConvention : IApplicationModelConvention
    {
        public void Apply(ApplicationModel application)
        {
            foreach (var controller in application.Controllers)
            {
                var type = controller.ControllerType;
                if (typeof(IDynamicApi).IsAssignableFrom(type) || type.IsDefined(typeof(DynamicApiAttribute), true))
                {
                    ClearAction(controller);
                    ConfigureApiExplorer(controller);
                    ConfigureSelector(controller);
                }
            }
        }

        private void ClearAction(ControllerModel controller)
        {
            Type genericBaseType = AssemblyUtils.GetGenericTypeByName("BaseService`1");
            var needRemoveAction = controller.Actions
                .Where(action => !action.ActionMethod.DeclaringType.IsDerivedFromGenericBaseRepository(genericBaseType))
                .ToList();

            foreach (var actionModel in needRemoveAction)
            {
                controller.Actions.Remove(actionModel);
            }
        }


        private static void ConfigureApiExplorer(ControllerModel controller)
        {
            if (!controller.ApiExplorer.IsVisible.HasValue)
                controller.ApiExplorer.IsVisible = true;

            foreach (var action in controller.Actions)
            {
                if (!action.ApiExplorer.IsVisible.HasValue)
                {
                    action.ApiExplorer.IsVisible = true;
                }
            }
        }

        private void ConfigureSelector(ControllerModel controller)
        {
            RemoveEmptySelectors(controller.Selectors);

            if (controller.Selectors.Any(selector => selector.AttributeRouteModel != null))
                return;

            foreach (var action in controller.Actions)
            {
                ConfigureSelector(action);
            }
        }

        private static void RemoveEmptySelectors(IList<SelectorModel> selectors)
        {
            for (var i = selectors.Count - 1; i >= 0; i--)
            {
                var selector = selectors[i];
                if (selector.AttributeRouteModel == null &&
                   (selector.ActionConstraints == null || selector.ActionConstraints.Count <= 0) &&
                   (selector.EndpointMetadata == null || selector.EndpointMetadata.Count <= 0))
                {
                    selectors.Remove(selector);
                }
            }
        }

        private void ConfigureSelector(ActionModel action)
        {
            RemoveEmptySelectors(action.Selectors);

            if (action.Selectors.Count <= 0)
                AddServiceSelector(action);
            else
                NormalizeSelectorRoutes(action);
        }

        private void AddServiceSelector(ActionModel action)
        {
            var template = new Microsoft.AspNetCore.Mvc.RouteAttribute(GetRouteTemplate(action));
            var selector = new SelectorModel
            {
                AttributeRouteModel = new AttributeRouteModel(template)
            };
            selector.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { GetHttpMethod(action) }));
            action.Selectors.Add(selector);
        }

        private void NormalizeSelectorRoutes(ActionModel action)
        {
            foreach (var selector in action.Selectors)
            {
                var template = new Microsoft.AspNetCore.Mvc.RouteAttribute(GetRouteTemplate(action, selector));
                selector.AttributeRouteModel = new AttributeRouteModel(template);
                if (selector.ActionConstraints.OfType<HttpMethodActionConstraint>().FirstOrDefault()?.HttpMethods?.FirstOrDefault() == null)
                    selector.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { GetHttpMethod(action) }));

            }
        }

        private string GetRouteTemplate(ActionModel action, SelectorModel selectorModel = null)
        {
            var routeTemplate = new StringBuilder();
            var names = action.Controller.ControllerType.Namespace.Split('.');
            if (names.Length > 2)
            {
                routeTemplate.Append(names[^2]);
            }

            // Controller
            var controllerName = action.Controller.ControllerName;
            if (controllerName.EndsWith("Service"))
                controllerName = controllerName[0..^7];

            if (selectorModel is { AttributeRouteModel: not null })
            {
                if (!string.IsNullOrWhiteSpace(selectorModel.AttributeRouteModel?.Template))
                {
                    if (selectorModel.AttributeRouteModel.Template.StartsWith("/"))
                    {
                        routeTemplate.Append(selectorModel.AttributeRouteModel.Template);
                    }
                    else
                    {
                        routeTemplate.Append($"{BaseRoute}/{controllerName}/{selectorModel.AttributeRouteModel.Template}");
                    }
                }
            }
            else
            {
                routeTemplate.Append($"{BaseRoute}/{controllerName}");

                // Action
                var actionName = action.ActionName;
                if (actionName.EndsWith("Async") || actionName.EndsWith("async"))
                    actionName = actionName[..^"Async".Length];

                if (!string.IsNullOrEmpty(actionName))
                {
                    routeTemplate.Append($"/{RemoveHttpMethodPrefix(actionName)}");
                }
            }


            return routeTemplate.ToString();
        }

        private static string GetHttpMethod(ActionModel action)
        {
            var actionName = action.ActionName.ToLower();
            string Method = string.Empty;
            if (!string.IsNullOrEmpty(actionName))
            {
                Method = GetName(actionName);
            }
            return Method;
        }

        private static string GetName(string actionName)
        {
            string result = "GET";
            foreach (string key in Methods.Keys)
            {
                if (actionName.Contains(key))
                {
                    result = Methods[key];
                    break;
                }

            }
            return result;
        }
        internal static Dictionary<string, string> Methods { get; private set; }
        internal static string BaseRoute { get; private set; } = "api";
        static ApiConvention()
        {
            Methods = new Dictionary<string, string>()
            {

                ["get"] = "GET",
                ["find"] = "GET",
                ["fetch"] = "GET",
                ["query"] = "GET",
                ["post"] = "POST",
                ["add"] = "POST",
                ["create"] = "POST",
                ["insert"] = "POST",
                ["submit"] = "POST",
                ["put"] = "POST",
                ["update"] = "POST",
                ["delete"] = "DELETE",
                ["remove"] = "DELETE",
                ["clear"] = "DELETE",
                ["patch"] = "PATCH"
            };

        }
        private static string RemoveHttpMethodPrefix(string actionName)
        {
            foreach (var method in Methods.Keys)
            {
                if (actionName.StartsWith(method, StringComparison.OrdinalIgnoreCase))
                {
                    // 移除前缀并返回结果
                    return actionName.Substring(method.Length);
                }
            }

            return actionName; // 如果没有找到前缀，返回原始名称
        }
    }

}
